﻿using System;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;

namespace Common.MVVM.Core.Base
{
    public abstract class ViewModelBase<T> : ViewModelBase
    {
        public T Model
        { get; protected set; }

        /// <summary>
        /// Attach to the Collection changed event and update the base list.
        /// </summary>
        /// <typeparam name="V">The type of the observable collection to watch.</typeparam>
        /// <param name="collection">The observable collection to watch.</param>
        /// <param name="list">The list to update.</param>
        protected virtual void UpdateBaseListOnChange<V, K>(ObservableCollection<V> collection, GetList list) where V: ViewModelBase<K>
        {
            collection.CollectionChanged += (sender, e) =>
                {
                    IList baseList = list.Invoke();
                    switch (e.Action)
                    {
                        case NotifyCollectionChangedAction.Add:
                            foreach (V item in e.NewItems)
                            {
                                baseList.Add(item.Model);
                            }
                            break;
                        case NotifyCollectionChangedAction.Remove:
                            foreach (V item in e.OldItems)
                            {
                                baseList.Remove(item.Model);
                            }
                            break;
                        case NotifyCollectionChangedAction.Replace:
                            ViewModelBase<K> newItem = (ViewModelBase<K>)((e.NewItems.Count == 0) ? null : e.NewItems[0]);
                            baseList.RemoveAt(e.OldStartingIndex);
                            baseList.Insert(e.NewStartingIndex, newItem.Model);
                            break;
                        case NotifyCollectionChangedAction.Move:
                            ViewModelBase<K> oldItem = (ViewModelBase<K>)((e.OldItems.Count == 0) ? null : e.OldItems[0]);
                            baseList.RemoveAt(e.OldStartingIndex);
                            baseList.Insert(e.NewStartingIndex, oldItem.Model);
                            break;
                        case NotifyCollectionChangedAction.Reset:
                            baseList.Clear();
                            break;
                    }
                };
        }

        /// <summary>
        /// Set up an observable collection to automatically update a base list filled with models.
        /// </summary>
        /// <typeparam name="T">The type that the observable collection will contain.</typeparam>
        /// <typeparam name="K">The type that the base list contains.</typeparam>
        /// <param name="list">The base list.</param>
        /// <param name="convertToViewModel">A delegate to convert a model to a view model.</param>
        /// <returns></returns>
        protected virtual ObservableCollection<T> SetUpObservableCollection<T, K>(IList<K> list, ConvertObject convertToViewModel) where T: ViewModelBase<K>
        {
            ObservableCollection<T> collection = new ObservableCollection<T>();
            foreach (K item in list)
            {
                collection.Add((T)convertToViewModel.Invoke(item));
            }
            UpdateBaseListOnChange<T,K>(collection, () => { return (IList)list; });
            return collection;
        }


#if DEBUG
        /// <summary>
        /// Useful for ensuring that ViewModel objects are properly garbage collected.
        /// </summary>
        ~ViewModelBase()
        {
            var msg = string.Format("{0} ({1}) ({2}) Finalized", GetType().Name, DisplayName, GetHashCode());
            Debug.WriteLine(msg);
        }
#endif
    }

    public abstract class ViewModelBase: ObserveProperty, IDisposable
    {

        public virtual string DisplayName
        {
            get;
            set;
        }

        public void Dispose()
        {
            OnDispose();
        }

        protected virtual void OnDispose()
        {

        }

#if DEBUG
        /// <summary>
        /// Useful for ensuring that ViewModel objects are properly garbage collected.
        /// </summary>
        ~ViewModelBase()
        {
            var msg = string.Format("{0} ({1}) ({2}) Finalized", GetType().Name, DisplayName, GetHashCode());
            Debug.WriteLine(msg);
        }
#endif
    }

    public delegate IList GetList();
    public delegate object ConvertObject(object o);

}
